#include <vector>
#include "pipe_module.h"
#include "server.h"
#include "rpc/rpc_handle.hpp"
#include <string.h>
#include <cstdarg>

// pipe log module.
class CPipeLog : public anet::pipe::IPipeLog {
public:
	CPipeLog() : m_level(0) {}
	virtual ~CPipeLog() {}

public:
	virtual bool setLevel(int level) {
		m_level = level;
		return true;
	}
	virtual int getLevel() const { 
		return m_level;
	}
	virtual void Debug(const char* format, ...) {
		if (getLevel() <= int(anet::log::eLogLevel::debugLevel)) {
			va_list args;
			va_start(args, format);

			char buffer[1024];
			std::snprintf(buffer, sizeof(buffer), "%s %s:%d %s", anet::log::shortFileName(__FILE__), __FUNCTION__, __LINE__, format);
			anet::log::aLog::instance().ADebug(buffer, args);

			va_end(args);
		}
	}
	virtual void Info(const char* format, ...) {
		if (getLevel() <= int(anet::log::eLogLevel::infoLevel)) {
			va_list args;
			va_start(args, format);

			char buffer[1024];
			std::snprintf(buffer, sizeof(buffer), "%s %s:%d %s", anet::log::shortFileName(__FILE__), __FUNCTION__, __LINE__, format);
			anet::log::aLog::instance().AInfo(buffer, args);

			va_end(args);
		}
	}
	virtual void Warn(const char* format, ...) {
		if (getLevel() <= int(anet::log::eLogLevel::warnLevel)) {
			va_list args;
			va_start(args, format);

			char buffer[1024];
			std::snprintf(buffer, sizeof(buffer), "%s %s:%d %s", anet::log::shortFileName(__FILE__), __FUNCTION__, __LINE__, format);
			anet::log::aLog::instance().AWarn(buffer, args);

			va_end(args);
		}
	}
	virtual void Crit(const char* format, ...) {
		if (getLevel() <= int(anet::log::eLogLevel::critLevel)) {
			va_list args;
			va_start(args, format);

			char buffer[1024];
			std::snprintf(buffer, sizeof(buffer), "%s %s:%d %s", anet::log::shortFileName(__FILE__), __FUNCTION__, __LINE__, format);
			anet::log::aLog::instance().ACrit(buffer, args);

			va_end(args);
		}
	}

private:
	int m_level;
};

IMPLEMENT_SINGLETON(CPipeModule);
CPipeModule::CPipeModule() : m_log(nullptr), m_pipeModule(nullptr) {
}

CPipeModule::~CPipeModule() {
	if (m_log != nullptr) {
		delete m_log;
		m_log = nullptr;
	}
}

bool CPipeModule::Init(const char *pipeFilePath, anet::pipe::IPipeReporter *reporter) {
	if (reporter == nullptr) {
		return false;
	}

	if (nullptr == (m_pipeModule = anet::pipe::GetPipeModule())) {
		return false;
	}

	m_log = new CPipeLog();
	m_log->setLevel(int(anet::log::eLogLevel::debugLevel));
	anet::pipe::SetLog(m_log);
	return m_pipeModule->Init(pipeFilePath, reporter);
}

bool CPipeModule::Run(int count) {
	return m_pipeModule != nullptr && m_pipeModule->Run(count);
}

// server manager.
IMPLEMENT_SINGLETON(CServerMgr);
CServerMgr::CServerMgr() {
	m_servers.clear();
}

CServerMgr::~CServerMgr() {
	for (auto& pair : m_servers) {
		delete pair.second;
	}
	m_servers.clear();
}

void CServerMgr::OnReport(bool isConnected, anet::pipe::IPipe* pipe) {
	if (pipe == nullptr) {
		LogACrit("pipe object is null");
		return;
	}

	auto it = m_servers.find(pipe->GetRemotePipeId());
	if (isConnected) {
		CServer* pServer = nullptr;
		if (it == m_servers.end()) {
			pServer = this->createServer(pipe);

			// Connection callback.
			LogAInfo("%s(%s) is connected",
				pipe->GetRemotePipeName().c_str(),
				anet::pipe::getPipeIdStr(pipe->GetRemotePipeId()).c_str());
		} else {
			it->second->SetPipe(pipe);
			pipe->SetMsgHandler(it->second);
			pServer = it->second;
		}
		pServer->onStatus(true);	
	} else {
		if (it != m_servers.end()) {
			// Terminate callback.
			LogAInfo("%s(%s) is disconnected",
				pipe->GetRemotePipeName().c_str(),
				anet::pipe::getPipeIdStr(pipe->GetRemotePipeId()).c_str());

			// onStatus callback.
			it->second->onStatus(false);
			it->second->SetPipe(nullptr);
		} else {
			LogACrit("can not find %s(%s) server",
				pipe->GetRemotePipeName().c_str(),
				anet::pipe::getPipeIdStr(pipe->GetRemotePipeId()).c_str());
		}
	}
}

CServer* CServerMgr::createServer(anet::pipe::IPipe* pipe) {
	if (pipe == nullptr) {
		return nullptr;
	}

	auto res = anet::pipe::extractPipe(pipe->GetRemotePipeId());
	auto type = std::get<2>(res);

	// Try to create new server with type.
	CServer* pServer = nullptr;
	if (type == DBType) {
		pServer = new CServer(CDBServerHandler::instance());
	} else if (type == GameType) {
		pServer = new CServer(CGameServerHandler::instance());
	} else {
		LogACrit("Can not find %s(%s) pipe name", 
			pipe->GetRemotePipeName().c_str(),
			anet::pipe::getPipeIdStr(pipe->GetRemotePipeId()).c_str());
		return pServer; // return nullptr.
	}

	// Initialize server and its handler.
	pServer->SetPipe(pipe);
	m_servers[pipe->GetRemotePipeId()] = pServer;
	pipe->SetMsgHandler(pServer);
	return pServer;
}

CServer* CServerMgr::findDBServer() {
	return this->findType(DBType);
}

CServer* CServerMgr::findGameServer() {
	return this->findType(GameType);
}

CServer* CServerMgr::findDBServerWithId(unsigned int id) {
	auto it = m_servers.find(id);
	if (it != m_servers.end()) {
		return it->second;
	}
	return nullptr;
}

CServer* CServerMgr::findGameServerWithId(unsigned int id) {
	auto it = m_servers.find(id);
	if (it != m_servers.end()) {
		return it->second;
	}
	return nullptr;
}

CServer* CServerMgr::findType(int TYPE) {
	for (auto it = m_servers.begin(); it != m_servers.end(); it++) {
		if (it->second != nullptr) {
			if (auto pipe = it->second->GetPipe(); pipe != nullptr) {
				auto pipeId = anet::pipe::extractPipe(pipe->GetRemotePipeId());
				if (std::get<2>(pipeId) == TYPE) {
					return it->second;
				}
			}
		}
	}
	return nullptr;
}

void CServer::Handle(const char* msg, int len) {
	if (m_iHandler != nullptr && msg != nullptr && len >= 0) {
		m_iHandler->Handle(msg, len, this);
	} else {
		LogACrit("Can not find %p handler", this);
	}
}

bool CServer::Send(unsigned short msgId, const char* msg, int len) {
	std::vector<char> vecBuf;
	vecBuf.resize(sizeof(unsigned short) + len);

	char* pBuf = &vecBuf[0];

	// message id.
	*(unsigned short*)(pBuf) = htons(msgId);

	// copy message.
	memcpy(pBuf + sizeof(msgId), msg, size_t(len));
	auto allLen = sizeof(msgId) + len;

	return this->Send(pBuf, int(allLen));
}

bool CServer::Send(const char* msg, int len) {
	if (msg == nullptr || len == 0) {
		return false;
	}
	if (m_pipe != nullptr) {
		m_pipe->Send(msg, len);
	}
	return m_pipe != nullptr;
}

void CServer::onStatus(bool isConnect) {
	if (m_iHandler != nullptr) {
	    m_iHandler->OnStatus(isConnect, this);
	} else {
		LogACrit("Can not find %p handler", this);
	}
}
